package org.ative.chs;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Map;

import org.apache.log4j.Logger;

public class UciUtils {

	private static Logger logger = Logger.getLogger(UciUtils.class.getName());
	
	//TODO check that the string is an option string
	/**
	 * <b>parseOptionString</b><p/>
	 * <code>public UciOption <b>parseOptionString</b>(String optionString)</code><p/>
	 * Tokenizes an Option string and populates a UciOption object.
	 * @param optionString	String starting with "option" from engine output
	 * @return a {@link UciOption} object containing all of the information for this option
	 */
	public static UciOption parseOptionString(String optionString) {
		UciStringTokenizer st = new UciStringTokenizer(optionString
				.substring(6).trim());
		StringBuilder optionName = new StringBuilder();
		String optionType = "";
		String optionMin = "";
		String optionMax = "";
		String optionDefault = "";
		ArrayList<String> optionVar = new ArrayList<String>();

		while (st.hasMoreTokens()) {
			String token = st.nextToken();
			if (token.equals("name")) {
				optionName.append(st.nextToken());
				// if it has a name, it has to have a type after it
				// so this will always complete
				while (true) {
					String nextToken = st.nextToken();
					if (nextToken.equals("type")) {
						optionType = st.nextToken();
						break;
					} else {
						optionName.append(" " + nextToken);
					}
				}
			} else if (token.equals("min")) {
				optionMin = st.nextToken();
			} else if (token.equals("max")) {
				optionMax = st.nextToken();
			} else if (token.equals("var")) {
				optionVar.add(st.nextToken());
			} else if (token.equals("default")) {
				optionDefault = st.nextToken();
			}
		}

		UciOption uciOption = new UciOption(optionName.toString(), optionType,
				optionMin, optionMax, optionDefault, optionVar);

		return uciOption;
	}

	public static void parseIdString(UciId uciId, String idString) {
		String id = idString.substring(3).trim();
		String author = "";
		String engineName = "";
		if (id.startsWith("author")) {
			author = (id.substring(6).trim());
			uciId.setAuthor(author);
		} else if (id.startsWith("name")) {
			engineName = (id.substring(4).trim());
			uciId.setName(engineName);
		}
	}

	public static UciBestMove parseBestMoveString(String bestMoveString) {

		UciStringTokenizer st = new UciStringTokenizer(bestMoveString);

		String bestMove = "";
		String ponder = "";

		while (st.hasMoreTokens()) {
			String token = st.nextToken();
			if (token.equals("bestmove")) {
				bestMove = st.nextToken();
			} else if (token.equals("ponder")) {
				ponder = st.nextToken();
			}
		}

		UciBestMove uciBestMove = new UciBestMove(bestMove, ponder);
		return uciBestMove;
	}

	public static void handleInfoString(Map<Integer, Writer> oswMap, UciInfo uciInfo, String infoString) 
			throws IOException {
		// first thing is to check if this is a MultiPV string
		UciStringTokenizer multipvCheck = new UciStringTokenizer(infoString);
		String token = null;
		boolean multipvFlag = false;
		while(multipvCheck.hasMoreTokens()) {
			token = multipvCheck.nextToken();
			if (token.equals("multipv")) {
				multipvFlag = true;
			}
		}
		
		if (multipvFlag) {
			// if this is a multipv line, parse it
			UciMultiPv uciMultiPv = parseMultiPvString(oswMap, infoString);
			// add it to the multipv map
			uciInfo.setMultiPvMap(uciMultiPv);
		} else {
			parseInfoString(oswMap, uciInfo, infoString);
		}
	}

	public static UciMultiPv parseMultiPvString(Map<Integer, Writer> oswMap, String multiPvString) 
			throws IOException {
		UciStringTokenizer st = new UciStringTokenizer(multiPvString.substring(4).trim());
		String token = null;
		UciMultiPv uciMultiPv = new UciMultiPv();
		
		while(st.hasMoreTokens()){
			token = st.nextToken();
			if (token.equals("depth")) {
				uciMultiPv.setDepth(st.nextToken());
			} else if (token.equals("currmove")) {
				uciMultiPv.setCurrmove(st.nextToken());
			} else if (token.equals("currmovenumber")) {
				uciMultiPv.setCurrmovenumber(st.nextToken());
			} else if (token.equals("seldepth")) {
				uciMultiPv.setSeldepth(st.nextToken());
			} else if (token.equals("score")) {
				UciScore uciScore = new UciScore();
				String type = st.nextToken();
				if (type.equals("cp")) {
					uciScore.setCp();
					uciScore.setValue(st.nextToken());
				} else if (type.equals("mate")) {
					uciScore.setMate();
					uciScore.setValue(st.nextToken());
				}
				uciMultiPv.setScore(uciScore);
			} else if (token.equals("upperbound")) {
				uciMultiPv.setScoreIsUpperbound();
			} else if (token.equals("lowerbound")) {
				uciMultiPv.setScoreIsLowerbound();
			} else if (token.equals("nodes")) {
				uciMultiPv.setNodes(st.nextToken());
				writeOsw(oswMap, UciStreamNames.NODES.ordinal(), uciMultiPv.getNodes());
			} else if (token.equals("nps")) {
				uciMultiPv.setNps(st.nextToken());
				writeOsw(oswMap, UciStreamNames.NPS.ordinal(), uciMultiPv.getNps());
			} else if (token.equals("time")) {
				uciMultiPv.setTime(st.nextToken());
			} else if (token.equals("sbhits")) {
				uciMultiPv.setSbhits(st.nextToken());
			} else if (token.equals("tbhits")) {
				uciMultiPv.setTbhits(st.nextToken());
			} else if (token.equals("hashfull")) {
				uciMultiPv.setHashfull(st.nextToken());
			} else if (token.equals("cpuload")) {
				uciMultiPv.setCpuload(st.nextToken());
			} else if (token.equals("pv")) {
				StringBuilder sb = new StringBuilder();
				while (st.hasMoreTokens()) {
					sb.append(st.nextToken() + " ");
				}
				uciMultiPv.setPv(sb.toString().trim());
				// prefix the PV with the engine ID so the gui can order it. 
				// you always have the PV id by now. 
				writeOsw(oswMap, UciStreamNames.PV.ordinal(), 
						uciMultiPv.getMultiPvId() + ": " + uciMultiPv.getPv());
			} else if (token.equals("multipv")) {
				uciMultiPv.setMultiPvId(st.nextToken());
			}
		}
		if(uciMultiPv.getMultiPvId().equals("1")) {
			writeOsw(oswMap, UciStreamNames.TOPEVAL.ordinal(), uciMultiPv.getScore().getValue());
		}
		return uciMultiPv;
	}
	
	public static void parseInfoString(Map<Integer, Writer> oswMap, UciInfo uciInfo, String infoString) 
			throws IOException{
		UciStringTokenizer st = new UciStringTokenizer(infoString.substring(4).trim());
		String token = null;
		
		while (st.hasMoreTokens()){
			token = st.nextToken();
			if (token.equals("depth")) {
				String depth = st.nextToken();
				uciInfo.setDepth(depth);
				writeOsw(oswMap, UciStreamNames.DEPTH.ordinal(),uciInfo.getDepth());
			} else if (token.equals("currmove")) {
				String currmove = st.nextToken();
				uciInfo.setCurrmove(currmove);
				writeOsw(oswMap, UciStreamNames.CURRMOVE.ordinal(), uciInfo.getCurrmove());		
			} else if (token.equals("currmovenumber")) {
				uciInfo.setCurrmovenumber(st.nextToken());
			} else if (token.equals("cpuload")) {
				uciInfo.setCpuload(st.nextToken());
			} else if (token.equals("string")) {
				StringBuilder sb = new StringBuilder();
				while (st.hasMoreTokens()) {
					sb.append(st.nextToken() + " ");
				}
				uciInfo.setString(sb.toString());
			} else if (token.equals("refutation")) {
				StringBuilder sb = new StringBuilder();
				while (st.hasMoreTokens()) {
					sb.append(st.nextToken() + " ");
				}
				uciInfo.setRefutation(sb.toString());
			} 
			//TODO add currline for shredder
		}
	}
	//TODO add copyprotection
	//TODO add registration
	public static void writeOsw(Map<Integer, Writer> oswMap, int stream, String value) 
			throws IOException {
		oswMap.get(stream).write(value + "\n");
		oswMap.get(stream).flush();
	}
}
